package handler

import (
	"context"
	"crypto/md5"
	"fmt"
	"github.com/anaskhan96/go-password-encoder"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	"gorm.io/gorm"
	"mxshop/user_srv/global"
	"mxshop/user_srv/model"
	"mxshop/user_srv/proto/proto_go"
	"strconv"
	"strings"
	"time"
)

type UserServer struct {
	proto_go.UnsafeUserServer
}

func Model2Responses(user model.User) *proto_go.UserInfoResponse {
	userInfo := &proto_go.UserInfoResponse{
		Id:       user.ID,
		Password: user.Password,
		Mobile:   user.Mobile,
		NickName: user.NickName,
		Gender:   user.Gender,
		Role:     uint32(user.Role),
	}
	if user.Birthday != nil {
		userInfo.Birthday = uint64(user.Birthday.Unix())
	}
	return userInfo
}

// GetUserList 获取用户列表
func (s *UserServer) GetUserList(c context.Context, in *proto_go.PageInfo) (*proto_go.UserListResponse, error) {
	var users []model.User
	var total int64
	result := global.DB.Model(&model.User{}).Count(&total)
	if result == nil {
		return nil, result.Error
	}
	rsp := &proto_go.UserListResponse{}
	rsp.Total = int32(total)
	global.DB.Scopes(Paginate(int(in.Pn), int(in.PSize))).Find(&users)
	for _, user := range users {
		responses := Model2Responses(user)
		rsp.Data = append(rsp.Data, responses)
	}
	return rsp, nil
}

// GetUserByMobile 通过手机号获取用户信息
func (s *UserServer) GetUserByMobile(c context.Context, mobile *proto_go.MobileRequest) (*proto_go.UserInfoResponse, error) {
	var userInfo = &model.User{}
	result := global.DB.Where(&model.User{Mobile: mobile.Mobile}).First(userInfo)
	if result.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "用户不存在")
	}
	if result.Error != nil {
		return nil, result.Error
	}
	responses := Model2Responses(*userInfo)
	return responses, nil
}

// GetUserById 通过id获取用户信息
func (s *UserServer) GetUserById(c context.Context, IdRequest *proto_go.IdRequest) (*proto_go.UserInfoResponse, error) {
	var userInfo = &model.User{}
	id, _ := strconv.ParseInt(IdRequest.Id, 10, 32)
	userInfo.ID = int32(id)
	result := global.DB.First(userInfo)
	if result.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "用户不存在")
	}
	if result.Error != nil {
		return nil, result.Error
	}
	responses := Model2Responses(*userInfo)
	return responses, nil
}

// CreateUser 创建用户
func (s *UserServer) CreateUser(c context.Context, req *proto_go.CreateUserInfo) (*proto_go.UserInfoResponse, error) {
	var user model.User
	result := global.DB.Where(&model.User{Mobile: req.Mobile}).First(&user)
	if result.RowsAffected == 1 {
		return nil, status.Errorf(codes.AlreadyExists, "用户已存在")
	}
	user.Mobile = req.Mobile
	user.NickName = req.NickName

	options := &password.Options{10, 1000, 30, md5.New}
	salt, encodedPwd := password.Encode(req.Password, options)
	newPassword := fmt.Sprintf("$mysaltpwd$%s$%s", salt, encodedPwd)
	user.Password = newPassword
	result = global.DB.Create(&user)
	if result.Error != nil {
		return nil, status.Errorf(codes.Internal, result.Error.Error())
	}
	responses := Model2Responses(user)
	return responses, nil
}

// UpdateUser 更新用户
func (s *UserServer) UpdateUser(c context.Context, info *proto_go.UpdateUserInfo) (*emptypb.Empty, error) {
	var user model.User
	user.ID = info.Id
	result := global.DB.First(&user)
	if result.RowsAffected != 1 {
		return nil, status.Errorf(codes.NotFound, "未找到用户")
	}
	if result.Error != nil {
		return nil, result.Error
	}
	unix := time.Unix(int64(info.Birthday), 0)
	m := map[string]interface{}{
		"NickName": info.NickName,
		"Birthday": &unix,
		"Gender":   info.Gender,
	}
	res := global.DB.Model(&model.User{}).Where("id=?", 1).Updates(m)
	if res.RowsAffected != 1 {
		return nil, status.Errorf(codes.Internal, res.Error.Error())
	}
	return &emptypb.Empty{}, nil
}

// CheckPassWord 检查密码
func (s *UserServer) CheckPassWord(c context.Context, req *proto_go.PassWordInfo) (*proto_go.CheckResponse, error) {
	split := strings.Split(req.EncryptedPassword, "$")
	options := &password.Options{10, 1000, 30, md5.New}
	check := password.Verify(req.Password, split[2], split[3], options)
	var checkResponse proto_go.CheckResponse
	checkResponse.Success = check
	return &checkResponse, nil
}

func (s *UserServer) mustEmbedUnimplementedUserServer() {

}

// Paginate 分页
func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {
		if page == 0 {
			page = 1
		}
		switch {
		case pageSize > 100:
			pageSize = 100
		case pageSize <= 0:
			pageSize = 10
		}
		offset := (page - 1) * pageSize
		return db.Offset(offset).Limit(pageSize)
	}
}
